import json
import random
from pathlib import Path
from dataclasses import dataclass
from typing import NamedTuple, Dict, Literal, Tuple, cast


class PunchProfile(NamedTuple):
    choices: int
    threshold: int
    hit_damage: int
    block_damage: int

    pre_msg: str
    hit_msg: str
    blocked_msg: str

    knockout_possible: bool = False

    def is_hit(self):
        return random.randint(1, self.choices) <= self.threshold


@dataclass
class Player:
    name: str
    best: int
    weakness: int
    HP: int
    initial_hp: int
    # 通过is_computer判断是出击对象
    is_computer: bool
    punch_profiles: Dict[Literal[1, 2, 3, 4], PunchProfile]
    damage: int = 0
    score: int = 0
    continue_turn: int = 0
    knockedout: bool = False

    def get_punch_choice(self) -> Literal[1, 2, 3, 4]:
        # 生成电脑的拳法通过随机数
        if self.is_computer:
            return random.randint(1, 4)
        else:
            punch = -1
        # 通过while死循环要求玩家必须选择1-4中一个数字
        while punch not in [1, 2, 3, 4, ]:
            print(f"{self.name}的招式是?")
            punch = int(input())
            if punch not in [1, 2, 3, 4, ]:
                print("请玩家重新选择拳击信息：")
                punch = int(input())
            return punch


# 击倒阈值
KNOCKOUT_THRESHOLD = 80
# 击晕信息
KNOCKOUT_COLD = "{loser}被击晕了无法正常比赛，胜利属于{winner}"


# 获取选手弱点信息
def get_vulnerability(player_best):
    print("请选择玩家的弱点是：")
    # 对于数字的判断
    vulnerability = int(input())
    if vulnerability not in [1, 2, 3, 4]:
        if vulnerability == player_best:
            print("请不要破坏规则，重新输入！")
            vulnerability = int(input())
    return vulnerability


# 获取电脑弱点信息
def get_opponent_stats() -> Tuple[int, int]:
    opponent_best = 0
    opponent_weakness = 0
    while opponent_best == opponent_weakness:
        opponent_best = random.randint(1, 4)
        opponent_weakness = random.randint(1, 4)
        return opponent_best, opponent_weakness

    # 获取招式基本信息


def read_punch_profiles(filepath: Path):
    with open(filepath, encoding='utf-8') as f:
        punch_profile_dict = json.load(f)
    return {
        cast(Literal[1, 2, 3, 4], int(key)): PunchProfile(**value)
        for key, value in punch_profile_dict.items()
    }


def main() -> None:
    print("拳击游戏")
    print("desgin by zct")
    print("由zct设计创建")
    print("202312060341")
    print("---------------------------")
    print("这个游戏采取奥运规则，三局两胜")
    print("期待你通过策略与运气赢得比赛！")
    print("玩家名字为：")
    player_name = input()
    print("对手名字为：")
    opponent_name = input()

    print("不同的出拳策略 1.摆拳 2.平勾拳 3.上钩拳 4.刺拳 ")
    print("从1-4选项中请选择玩家擅长的招式：")
    player_best = int(input())
    while player_best not in [1, 2, 3, 4]:
        print("请选择正确的选项!")
        player_best = int(input())
    player_weakness = get_vulnerability(player_best)
    player = Player(
        name=player_name,
        best=player_best,
        weakness=player_weakness,
        HP=random.randint(100, 120),
        initial_hp=0,
        continue_turn=0,
        is_computer=False,

        punch_profiles=read_punch_profiles(
            Path(__file__).parent / "player-profile.json"
        ),
    )

    opponent_best, opponent_weakness = get_opponent_stats()
    opponent = Player(
        name=opponent_name,
        best=opponent_best,
        weakness=opponent_weakness,
        HP=random.randint(100, 120),
        initial_hp=0,
        is_computer=True,
        continue_turn=0,
        punch_profiles=read_punch_profiles(
            Path(__file__).parent / "opponent-profile.json"
        ),
    )
    print(player)
    print(opponent)
    opponent.initial_hp = opponent.HP
    player.initial_hp = player.HP

    print("比赛正式开始，请做好准备！")
    for round_number in (1, 2, 3):
        if player.knockedout or opponent.knockedout:
            break
        play_round(round_number, player, opponent)

    if player.knockedout:
        print(KNOCKOUT_COLD.format(loser=player.name, winner=opponent.name))
    elif opponent.knockedout:
        print(KNOCKOUT_COLD.format(loser=opponent.name, winner=player.name))
    elif opponent.score > player.score:
        print(f"{opponent.name}打败了{player.name}")
    else:
        print(f"{player.name}打败了{opponent.name}")

    print("\n\n比赛结束")
    print("感谢游玩")


# 生成1个1-10的随机数，如果数字大于5则是对面回合
def is_opponents_turn() -> bool:
    return random.randint(1, 10) > 5


def play_round(round_number: int, player: Player, opponent: Player) -> None:
    print(f"    第{round_number}轮次\n-----开始------\n")
    #使每回合开始玩家生命值重置，initial_hp存储初始生命值
    if round_number == 1:
        print(f"这一轮{player.name} 初始血条为{player.HP}")
        print(f"这一轮{opponent.name} 初始血条为{opponent.HP}")
    else:
        player.HP = player.initial_hp
        opponent.HP = opponent.initial_hp
        print(f"{player.name} 初始血条为{player.HP}")
        print(f"{opponent.name} 初始血条为{opponent.HP}")
    # 三局两胜的规则，如果有一方的分数>=2，则直接结束
    if opponent.score >= 2 or player.score >= 2:
        return
    opponent.continue_turn = 0
    player.continue_turn = 0
    #最大为回合7
    for _action in range(7):

        opponents_turn = is_opponents_turn()
        print("-----------------------------")
        if is_opponents_turn():
            #print("111")
            opponent.continue_turn+=1
            #print(f"{opponent.name}的第{opponent.continue_turn}.1次攻击")
            opponents_turn = True
            while opponent.continue_turn >2:
                #print("222")
                opponents_turn = False
                opponent.continue_turn=0
                #print(f"{player.name}的第{player.continue_turn}.2次攻击")

        else:
            #print("333")
            player.continue_turn+= 1
            #print(f"{player.name}的第{player.continue_turn}.3次攻击")
            opponents_turn=False
            while player.continue_turn >2:
                #print("444")
                opponents_turn = True
                player.continue_turn=0
                #print(f"{opponent.name}的第{opponent.continue_turn}.4次攻击")

        # 不用再区分是玩家攻击还是电脑攻击，只做谁主动谁被动的判断

        if opponents_turn == True:
            punch = opponent.get_punch_choice()
            active = opponent
            passive = player
        else:
            punch = player.get_punch_choice()
            active = player
            passive = opponent
        punch_profile = active.punch_profiles[punch]
        print(punch_profile.pre_msg.format(opponent=opponent, player=player))
        # 如果选中的招式是擅长的招式则攻击+2
        if punch == active.best:
            active.damage = active.damage + 2

        # 如果对手不擅长这个招数，那么他将总是被击中
        if passive.weakness == punch or punch_profile.is_hit():
            print(punch_profile.hit_msg.format(opponent=opponent, player=player))
            # 判定是否存在直接击倒的可能性
            if punch_profile.knockout_possible and active.damage > KNOCKOUT_THRESHOLD:
                if opponents_turn and opponent.damage > KNOCKOUT_THRESHOLD and opponent.HP != 0:
                    player.knockedout = True
                    player.HP = 0
                    print(f"哦，不，看啊！！！{player.name}被击晕了，血条值骤减为{player.HP}")
                    break
                elif opponents_turn == False and player.damage > KNOCKOUT_THRESHOLD and player.HP != 0:
                    opponent.knockedout = True
                    opponent.HP = 0
                    print(f"哦，不，看啊！！！{opponent.name}被击晕了，血条值骤减为{opponent.HP}")
                    break
            # 如果不存在击倒可能性则记录伤害
            if opponents_turn == True:
                opponent.damage = opponent.damage + punch_profile.hit_damage
                player.HP = player.HP - opponent.damage
                if player.HP <= 0:
                    player.HP = 0
                    print(f"{player.name}的血条为{player.HP}, {player.name}不再有战斗的能力")
                    break
                else:
                    print(f"{player.name}受伤了，血条值下降为{player.HP}")
            else:
                player.damage += punch_profile.hit_damage
                opponent.HP = opponent.HP - player.damage
                if opponent.HP <= 0:
                    opponent.HP = 0
                    print(f"{opponent.name}的血条为{opponent.HP}, {opponent.name}不再有战斗的能力")
                    break
                else:
                    print(f"{opponent.name}受伤了，血条值下降为{opponent.HP}")

        else:
            print(punch_profile.blocked_msg.format(opponent=opponent, player=player))
            # 当格挡对方攻击时，我们也会遭受余震
            if opponents_turn:
                opponent.damage += punch_profile.block_damage
                player.HP = player.HP - opponent.damage
                if player.HP>0:
                    print(f"{player.name}挡住了攻击，因为格挡血条值下降为{player.HP}")
                else:
                    player.HP=0
                    print(f"因为格挡攻击{player.name}的血条下降为{player.HP}, {player.name}不再有战斗的能力")
                    break

            else:
                player.damage += punch_profile.block_damage
                opponent.HP = opponent.HP - player.damage
                if opponent.HP>0:
                    print(f"{opponent.name}挡住了攻击，因为格挡血条值下降为{opponent.HP}")
                else:
                    opponent.HP=0
                    print(f"因为格挡攻击{opponent.name}的血条下降为{opponent.HP}, {opponent.name}不再有战斗的能力")
                    break
        # 如果有人被击晕，直接结束比赛
        if player.knockedout or opponent.knockedout:
            break
        if player.HP == 0:
            print(f"{player.HP}血条为0，显而易见这局{player.HP}已经没有比赛能力了")
            break
        elif opponent.HP == 0:
            print(f"{opponent.HP}血条为0，显而易见这局{opponent.HP}已经没有比赛能力了")
            break
    # 这里的分数用伤害统计，不太合理并且每一局的伤害会累计到下一局，也就是说如果你第一局造成大量伤害，第二局没造成伤害也有可能获胜
    if player.damage > opponent.damage:
        print(f"{player.name}获得了第{round_number}场胜利")
    else:
        print(f"{opponent.name}获得了第{round_number}场胜利")


if __name__ == "__main__":
    main()
